﻿/*
	Copyright (c) 2005 by Sean Kent. All rights reserved.
	Permission to use, copy, modify and distribute this software for any
	purpose is hereby granted without fee, provided this copyright and
	permissions notice appear in all copies and derivatives.
*/
#region Using directives

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;

#endregion

namespace Slobby.Common.NetworkFormatter {
	public class AssemblyTypeInformation {
		public AssemblyTypeInformation(Assembly sharedAssembly) {
			this.sharedAssembly = sharedAssembly;
			serializableTypes = GetSerializableTypes();
			typeIdMap = GetTypeIdMap();
			typeFieldsMap = GetTypeFieldsMap();
		}

		public Type GetTypeFromId(int id) {
			try {
				return serializableTypes[id];
			} catch (IndexOutOfRangeException) {
				throw new SerializationException("Type not found for ID: " + id);
			}
		}

		public int GetIdFromType(Type t) {
			try {
				return typeIdMap[t];
			} catch (KeyNotFoundException) {
				throw new SerializationException("Type (" + t + ") is not from assembly: " + sharedAssembly.FullName);
			}
		}

		public FieldInfo[] GetFieldsFromType(Type t) {
			try {
				return typeFieldsMap[t];
			} catch (KeyNotFoundException) {
				throw new SerializationException("Type (" + t + ") is not from assembly: " + sharedAssembly.FullName);
			}
		}

		public void AssertIsAssemblyType(Type t) {
			GetIdFromType(t);
		}

		public List<Type> GetSerializableTypes() {
			Type[] types = sharedAssembly.GetTypes();
			List<Type> serializableTypes = new List<Type>();

			//serializableTypes.Add(typeof(Object));

			foreach (Type t in types) {
				if (t.IsSerializable)
					serializableTypes.Add(t);
			}

			serializableTypes.Sort(delegate(Type x, Type y) { return x.FullName.CompareTo(y.FullName); });

			return serializableTypes;
		}

		public Dictionary<Type, int> GetTypeIdMap() {
			Dictionary<Type, int> typeIdMap = new Dictionary<Type, int>();
			List<Type> types = GetSerializableTypes();

			int id = 0;
			foreach (Type t in types) {
				typeIdMap.Add(t, id);
				++id;
			}

			return typeIdMap;
		}

		public Dictionary<Type, FieldInfo[]> GetTypeFieldsMap() {
			Dictionary<Type, FieldInfo[]> typeFieldsMap = new Dictionary<Type, FieldInfo[]>();
			List<Type> types = GetSerializableTypes();

			foreach (Type t in types) {
				typeFieldsMap.Add(t, GetSerializableFields(t));
			}

			return typeFieldsMap;
		}

		public FieldInfo[] GetSerializableFields(Type t) {
			FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
			List<FieldInfo> serializableFields = new List<FieldInfo>();

			foreach (FieldInfo f in fields) {
				if (!f.IsNotSerialized && f.FieldType.IsSerializable) {
					serializableFields.Add(f);
				}
			}

			serializableFields.Sort(delegate(FieldInfo x, FieldInfo y) { return x.Name.CompareTo(y.Name); });

			return serializableFields.ToArray();
		}

		private Dictionary<Type, FieldInfo[]> typeFieldsMap;
		private Dictionary<Type, int> typeIdMap;
		private List<Type> serializableTypes;
		private Assembly sharedAssembly;
	}
}
